package com.example.realm;

import com.example.entity.Account;
import com.example.service.AccountService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

/**
 * @author latin-xiao-mao
 * @date 2020/7/8 21:17
 * @description 在这里边定义我们自己的逻辑，必须要继承 AuthorizingRealm
 * 注意：写完 realm之后，我们必须要进行配置，才能起作用。我们配置在config包下面 ShiroConfig
 * @className AccountRealm
 */
public class AccountRealm extends AuthorizingRealm {


	@Autowired
	private AccountService accountService;

	/**
	 * 授权
	 * @param principalCollection
	 * @return
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

		// 获取当前登录的用户信息
		Subject subject = SecurityUtils.getSubject();
		Account account = (Account) subject.getPrincipal(); // 由于在上一步的认证中，已经将用户信息存入 principal 中了，所以此处从这里取出来就是Account

		// 设置角色
		Set<String> roles = new HashSet<>();
		roles.add(account.getRole());
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);

		// 设置权限
		info.addStringPermission(account.getPerms());
		return info;
	}


	/**
	 * 认证
	 * @param authenticationToken
	 * @return
	 * @throws AuthenticationException
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

		// 首先我们需要获取 UsernamePasswordToken， shiro用来封装用户登录信息，使用用户的登录信息来创建令牌 Token
		// 注意这个客户端传过来的token中已经封装了用户的密码，用户名等信息
		UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

		// 从传过来的 token 中获取到用户名，然乎根据这个用户名去 查询数据库
		Account account = accountService.findByUsername(token.getUsername());

		// 判断account 用户名 是否存在
		if (account != null){
			// 若存在，我们就构造一个 SimpleAuthenticationInfo()有参对象返回，在这个对象中我们需要验证密码是否正确
			// 判断数据库中查出来的密码是否与 token 中带过来的密码是否相同。这里我们只需要把数据库查出来的密码传给它即可，
			// shiro 会自动将这个密码与 token 中携带的密码进行比对的
			// 第一个参数：是说明你要查找的是哪一个帐号
			// 第二个参数：是你数据库中查出来的真实密码
			// 第三个参数：是固定的name
			return new SimpleAuthenticationInfo(account, account.getPassword(), getName());
		}
		return null; // 用户名不存在，shiro则会自动抛出一个 UnknownAccountException。。异常的
	}
}
